#pragma once

#include "../base/types.h"

OPEN_JLIB_NS


template<typename Host, int Max>
struct StateMachineFunction {

	static bool switchState (Host *host, int to, void *data) {
		auto &ctx = host->_sm_ctx;
		if (to == ctx.stat || to >= Max)
			return false;
		auto exitHandler = Host::__sm_fun.exitHandlers[ctx.stat];
		if (exitHandler)
			exitHandler(host, data);
		auto gotoHandler = Host::__sm_fun.gotoHandlers[ctx.stat][to];
		if (gotoHandler)
			gotoHandler(host, data);
		ctx.last = ctx.stat;
		ctx.stat = to;
		ctx.hint = -1;
		ctx.prio = -1;
		ctx.data = nullptr;
		auto intoHandler = Host::__sm_fun.intoHandlers[to];
		if (intoHandler)
			intoHandler(host, data);
	}

	static void updateStateMachine (Host *host, void *data) {
		auto &ctx = host->_sm_ctx;
		if (ctx.hint >= 0 && ctx.hint != ctx.stat)
			switchState(host, ctx.hint, ctx.data);
		auto stepHandler = Host::__sm_fun.stepHandlers[ctx.stat];
		if (stepHandler)
			stepHandler(host, data);
	}

	static bool switchStateHint (Host *host, int to, int priority, void *data) noexcept {
		auto &ctx = host->_sm_ctx;
		if (to == ctx.stat || priority <= ctx.prio)
			return false;
		ctx.hint = to;
		ctx.prio = priority;
		ctx.data = data;
		return true;
	}

}; // struct StateMachineFunction

CLOSE_JLIB_NS



// declare state-machine in class
//   Host: Host Class Full Name
//   Init: Initialize State Name
//   ...: Other State Names
#define DECLARE_SM(Host, Init, ...) \
private: \
	typedef int size_type; \
	typedef Host* host_type; \
	typedef const char* name_type; \
	typedef void* data_type; \
	typedef void (*func_type) (host_type, data_type); \
	constexpr static size_type SM_Size = ARGN(__VA_ARGS__) + 1; \
  template<typename _Host, int _Max> friend struct jlib::StateMachineFunction; \
  struct __StateMachineHandler { \
		enum { Init, __VA_ARGS__ }; \
		func_type gotoHandlers[SM_Size][SM_Size]; \
		func_type intoHandlers[SM_Size]; \
		func_type exitHandlers[SM_Size]; \
		func_type stepHandlers[SM_Size]; \
		__StateMachineHandler () noexcept; \
  }; \
	struct __StateMachineContext { \
		size_type stat = 0; \
		size_type last = -1; \
		size_type hint = -1; \
		size_type prio = -1; \
		data_type data = nullptr; \
	}; \
	static __StateMachineHandler __sm_fun; \
	__StateMachineContext _sm_ctx; \
public: \
	enum { Init, __VA_ARGS__ }; \
	inline bool switchState (size_type to, data_type data = nullptr) { \
		jlib::StateMachineFunction<Host, SM_Size>::switchState(this, to, data); \
	} \
	inline void updateStateMachine (data_type data = nullptr) { \
		jlib::StateMachineFunction<Host, SM_Size>::updateStateMachine(this, data); \
	} \
	inline bool switchStateHint (size_type to, size_type priority = 0, data_type data = nullptr) noexcept { \
		jlib::StateMachineFunction<Host, SM_Size>::switchStateHint(this, to, priority, data); \
  } \
	inline int currentState () noexcept { \
		return this->_sm_ctx.stat; \
	} \
	inline int lastState () noexcept { \
		return this->_sm_ctx.last; \
	} \
	inline int hintState () noexcept { \
		return this->_sm_ctx.hint; \
	}


// begin to define state-machine handlers
#define SM_BEGIN(Host) \
Host::__StateMachineHandler Host::__sm_fun; \
Host::__StateMachineHandler::__StateMachineHandler () noexcept { \
	typedef Host __host_type__;


// define handler from $From state to $To state
#define SM_GOTO_HANDLER(From, To) \
	;this->gotoHandlers[From][To] = [] (__host_type__ *thiz, void *data)


// define handler to $State state
#define SM_INTO_HANDLER(State) \
	;this->intoHandlers[State] = [] (__host_type__ *thiz, void *data)


// define handler from $State state
#define SM_EXIT_HANDLER(State) \
	;this->exitHandlers[State] = [] (__host_type__ *thiz, void *data)


// define handler step $State state
#define SM_STEP_HANDLER(State) \
	;this->stepHandlers[State] = [] (__host_type__ *thiz, void *data)


// close definition
#define SM_END(Host) ;}


#endif // JLIB_BASE_STATEMACHINE_H
